package solver;

import java.io.*;
import java.net.URL;
import java.util.Random;

public class SudokuSolver {

       
        // les lignes et colonnes 0 ne sont pas utilisees
        private int[][] grilleSolution = new int [10][10]; //solution
        private int[][] grilleEnCours = new int [10][10]; //grille de jeu
       
       
        /**
         * Initialise la grille de solution avec les valeurs de la grille de Jeu
         */
        public void initGrilleSolution ()
        {
                int i, j;

                for (i=1; i<=9; i++)
                        for (j=1; j<=9; j++) {
                                grilleSolution[i][j] = grilleEnCours[i][j];
                        }
               
                for (i=1; i<=9; i++) {
                        if (calculSolution (1, 1, i))
                                break;
                }
        }

        /**
         * Indique si une valeur est unique dans son petit carre
         * @param x ligne de la valeur
         * @param y colonne de valeur
         * @param v valeur
         * @return true si v est unique, false sinon
         */
        private boolean estUniqueDansPetitCarre (int x, int y, int v)
        {
                int origineXCarre, origineYCarre;

                origineXCarre = ((x-1) / 3) * 3 + 1;
                origineYCarre = ((y-1) / 3) * 3 + 1;

                int i,j;

                for (i=origineXCarre; i<=origineXCarre+2; i++)
                        for (j=origineYCarre; j<=origineYCarre+2; j++) {
                                if ((i == x) && (j == y)) continue;
                                if (grilleSolution[i][j] == v) return false;
                        }

                return true;
        }

        /**
         * Indique si une valeur est valide a une position donnee
         * @param x ligne de la case
         * @param y colonne de la case
         * @param v valeur
         * @return true si elle est bonne, false sinon
         */
        private boolean estValide (int x, int y, int v)
        {
                if (grilleSolution[x][y] != 0) return (v == grilleSolution[x][y]);

                int i,j;

                for (i=1; i<=9; i++) {
                        if (i==x) continue;
                        if (grilleSolution[i][y] == v) return false;
                }

                for (j=1; j<=9; j++) {
                        if (j==y) continue;
                        if (grilleSolution[x][j] == v) return false;
                }

                return estUniqueDansPetitCarre (x, y, v);
        }

        /**
         * Indique si, avec la valeur entree, on peut trouver une solution
         * Si oui, alors elle sera stockee dans grilleSolution
         * @param x ligne de la case dont on ajoute la valeur
         * @param y colonne de la case dont on ajoute la valeur
         * @param v valeur de la case
         * @return true si une solution a ete trouve, false sinon
         */
        private boolean calculSolution (int x, int y, int v)
        {
                if (! estValide (x, y, v)) return false;

                // on essaye
                grilleSolution[x][y] = v;

                // on a trouve UNE solution; on s'arrete la!
                if ((x==9) && (y==9)) return true;

                // appel recursif
                int i;
                if (y==9) {
                        for (i=1; i<=9; i++)
                                if (calculSolution (x+1, 1, i)) return true;

                        // la solution proposee ne marche pas; backtrack
                        if (grilleEnCours[x][y] == 0) grilleSolution[x][y] = 0;
                        return false;
                }

                for (i=1; i<=9; i++)
                        if (calculSolution (x, y+1, i)) return true;    

                // la solution proposee ne marche pas; backtrack
                if (grilleEnCours[x][y] == 0) grilleSolution[x][y] = 0;
                return false;
        }
       
        /**
         * Retourne une string avec la grille de jeu
         * @return string : grille de jeu
         */
        public String toStringEnCour(){
                return toStringGrille(grilleEnCours);
        }
       
        /**
         * Retourne une string avec la grille de solution
         */
        public String toStringSolution (){
                return toStringGrille(grilleSolution);
        }
       
        /**
         * Retourne une string d'une grille de sudoku
         * @param grille grille de sudoku
         * @return string de la grille
         */
        private String toStringGrille(int[][] grille){
                String res="";
                int i, j;

                for (i=1; i<=9; i++) {
                        for (j=1; j<=9; j++)
                                res += grille[i][j] + " ";

                        res += "\n";
                }
               
                return res;
        }

       /**
        * Choisi un probleme aleatoirement et initialise la grille de jeu avec celui-ci
        */
        public void initFichierAle() {
        	URL url = getClass().getResource("SudokuSolver.class");
        	File fichier = new File(url.getPath());
        	File repertoire = new File(fichier.getParent());
                  
            FilenameFilter filter = new FilenameFilter() {
				
				@Override
				public boolean accept(File dir, String name) {
					return name.endsWith(".txt");
				}
			};
			
			String[] listeProblemes = repertoire.list(filter);  
			Random random = new Random();
			String pathname = listeProblemes[random.nextInt(listeProblemes.length)];
								
			initFromFile(repertoire.getPath()+File.separator+pathname);
      
        }
        /**
         * Initialise la grille de jeu depuis un fichier
         * @param inputFile url du fichier contenant la grille
         */
        private void initFromFile (String inputFile)
        {
                int i, j;
                try{
                        BufferedReader buff = new BufferedReader(new FileReader(inputFile));

                        try {
                                String line;
                                // Lecture du fichier ligne par ligne.
                                for (i=1; i<=9; i++) {
                                        line = buff.readLine();
                                        for (j=1; j<=9; j++)
                                                grilleEnCours[i][j] = Character.getNumericValue(line.charAt(j-1));
                                }
                        } finally {
                                // dans tous les cas, on ferme nos flux
                                buff.close();
                        }
                } catch (IOException ioe) {
                        // erreur de fermeture des flux
                        System.out.println("Erreur -- " + ioe.toString());
                }
        }
       
        /**
         * Indique si la grille de jeu est terminee
         * @return true si elle l'est, false sinon
         */
        public boolean estFini() {
                for (int i=1; i<=9; i++)
                        for (int j=1; j<=9; j++)
                                if(grilleEnCours[i][j]==0)
                                        return false;

                return true;
        }
       
        /**
         * Indique si une valeur est deja presente
         * @param x ligne de la valeur
         * @param y colonne de la valeur
         * @param valeur valeur dont on veut tester la presence
         * @return true s'il y a deja la valeur, false sinon
         */
        private boolean estPresente(int x, int y, int valeur) {
                return grilleEnCours[x][y]==valeur;
        }
       
        /**
         * Ajoute une valeur � la grille de jeu si elle est valide
         * @param x ligne de la case a modifier
         * @param y colonne de la case a modifier
         * @param valeur valeur qu'on essaye d'ajouter
         * @return -2 si la valeur est absente et fausse
         *                 -1 si la valeur est presente et fausse
         *                  0 si la valeur est presente et juste
         *                  1 si la valeur est absente et juste
         */
        public int ajouteValeur(int x, int y, int valeur) {
                if(estPresente(x, y, valeur)){ //present et ...
                        if(estValide(x, y, valeur))//... valide
                                return 0;
                        else //... non valide
                                return -1;
                }
                else {//pas present et ...
                        if(estValide(x, y, valeur)) {//... valide
                                grilleEnCours[x][y] = valeur;
                                return 1;
                        }
                        else // ... non valide
                                return -2;
                }
        }
        
        public int getGrilleSolution(int i, int j) {
        	return grilleSolution[i][j];
        }
        
        public void setGrilleEnCours(int i, int j, int val) {
        	grilleEnCours [i][j] = val ;
        }
}